﻿using System;
using System.Collections.Generic;
using System.Collections.Concurrent;
using System.Collections.ObjectModel;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Web.Http;
using System.Web.Http.Description;
using log4net;
using Newtonsoft.Json;
using RestSharp;

namespace Ymatou.ServiceCenter
{
    /// <summary>
    /// 服务元数据管理器
    /// </summary>
    public static class ServiceMetadataManager
    {
        private static readonly object Locker = new object();

        private static readonly SynchronizedDictionary<string, ServiceMetadata> ServiceMetadatas = new SynchronizedDictionary<string, ServiceMetadata>();
        private static readonly SynchronizedDictionary<string, long> ServiceMetadataUpdated = new SynchronizedDictionary<string, long>();

        static ServiceMetadataManager()
        {
            DaemonThread.Run(AutoUpdateServiceMetadata, (ex) => { Logger.Error("AutoUpdateServiceMetadataException", ex); }, RpcClientConfigManager.GetServiceMetadataAutoUpdateInterval);
        }

        /// <summary>
        /// 自动更新服务元数据
        /// </summary>
        private static void AutoUpdateServiceMetadata()
        {
            IDictionary<string, long> dictionary = new Dictionary<string, long>();
            ServiceMetadataUpdated.Foreach((pair) =>
            {
                if (!dictionary.ContainsKey(pair.Key))
                    dictionary.Add(pair.Key, pair.Value);
            });

            foreach (KeyValuePair<string, long> pair in dictionary)
            {
                FullServiceMetadata(pair.Key, pair.Value, true);
            }

            ServiceClient.DefaultTimeOut = RpcClientConfigManager.GetCurrentClientConfig().HttpRequestTimeOut;
            Logger.Debug(string.Format("AutoUpdateServiceMetadata,DefaultTimeOut:{0},GetDefaultTimeOut:{1}", ServiceClient.DefaultTimeOut, ServiceClient.GetDefaultTimeOut()));
        }

        /// <summary>
        /// 填充服务元数据
        /// </summary>
        /// <param name="appId">应用编号</param>
        /// <param name="lastUpdateTicks">最后一次更新时间刻度</param>
        /// <param name="isAutoAction">是否为定时更新线程所调用</param>
        public static void FullServiceMetadata(string appId, long? lastUpdateTicks = null, bool isAutoAction = false)
        {
            try
            {
                IList<ServiceMetadata> list = null;
                try
                {
                    list = ServiceCenterFacade.GetServiceMetadatas(appId, lastUpdateTicks);
                    if (list != null && list.Count > 0)
                        SaveServiceMetadatasToLocalConfig(appId, list);
                    StringBuilder sb = new StringBuilder();
                    if (list != null && list.Count > 0)
                    {
                        sb.AppendLine();
                        foreach (ServiceMetadata item in list)
                        {
                            if (item == null)
                                continue;
                            sb.AppendLine(item.ToString());
                        }
                    }

                    Logger.Debug(string.Format("AutoUpdateServiceMetadata,AppId:{0},lastUpdateTicks:{1},count:{2},IsAutoAction:{3},Items:{4}", appId, lastUpdateTicks, list == null ? 0 : list.Count, isAutoAction, sb.ToString()));
                }
                catch (Exception ex)
                {
                    Logger.Error(string.Format("FullServiceMetadata:{0},从服务端加载元数据出现异常！", appId), ex);
                    if (!isAutoAction)
                    {
                        list = GetServiceMetadatasFromLocalConfig(appId);
                        Logger.Debug(string.Format("FullServiceMetadata:{0},从服务端加载元数据出现异常，已转为从本地加载", appId));
                    }
                }

                if (list == null || list.Count <= 0)
                {
                    ServiceMetadataUpdated[appId] = 0L;
                    return;
                }
                foreach (ServiceMetadata serviceMetadata in list)
                {
                    if (serviceMetadata == null)
                        continue;
                    string key = string.Format("{0}_{1}_{2}", serviceMetadata.AppId, serviceMetadata.ApiName,
                        serviceMetadata.HttpMethod);
                    if (!ServiceMetadatas.ContainsKey(key))
                    {
                        ServiceMetadatas.Add(key, serviceMetadata);
                    }
                    else
                    {
                        ServiceMetadatas[key] = serviceMetadata;
                    }
                }
                ServiceMetadataUpdated[appId] = list.OrderByDescending(m => m.LastUpdateTicks).First().LastUpdateTicks;
            }
            catch (Exception ex)
            {
                Logger.Error(string.Format("FullServiceMetadata:{0}", appId), ex);
            }
        }

        /// <summary>
        /// 从本地配置文件中获取元数据配置信息
        /// </summary>
        /// <param name="appId">应用编号</param>
        /// <returns></returns>
        private static IList<ServiceMetadata> GetServiceMetadatasFromLocalConfig(string appId)
        {
            string path = Utils.ServiceMetadataLocalConfigPath(appId);
            try
            {
                FileInfo fileInfo = new FileInfo(path);
                if (fileInfo.Directory != null && !fileInfo.Directory.Exists)
                {
                    lock (Locker)
                    {
                        if (fileInfo.Directory != null && !fileInfo.Directory.Exists)
                        {
                            fileInfo.Directory.Create();
                            File.WriteAllText(path, "");
                        }
                    }
                }                
                
                string json = File.ReadAllText(path);
                if (string.IsNullOrWhiteSpace(json))
                {
                    Logger.Debug(string.Format("从本地配置文件中没有获取到服务元数据！{0}", path));
                }
                return JsonConvert.DeserializeObject<List<ServiceMetadata>>(json);
            }
            catch (Exception ex)
            {
                Logger.Error(string.Format("从本地配置文件中获取服务元数据失败！{0}", path), ex);
                return null;
            }
        }

        /// <summary>
        /// 保存服务元数据到本地文件
        /// </summary>
        /// <param name="appId">应用编号</param>
        /// <param name="serviceMetadatas">服务元数据列表</param>
        private static void SaveServiceMetadatasToLocalConfig(string appId, IList<ServiceMetadata> serviceMetadatas)
        {
            try
            {
                if (string.IsNullOrWhiteSpace(appId) || serviceMetadatas == null || serviceMetadatas.Count <= 0)
                    return;
                string path = Utils.ServiceMetadataLocalConfigPath(appId);
                string json = JsonConvert.SerializeObject(serviceMetadatas, Formatting.Indented);

                FileWriter.Write(path, json);
            }
            catch (Exception ex)
            {
                Logger.Error(string.Format("保存元数据到配置文件:{0}.config中时出现异常！", appId), ex);
            }
        }

        /// <summary>
        /// 获取服务元数据
        /// </summary>
        /// <param name="appId">应用编号</param>
        /// <param name="apiName">接口名称</param>
        /// <param name="httpMethod">http方法</param>
        /// <returns></returns>
        public static ServiceMetadata GetServiceMetadata(string appId, string apiName, Method httpMethod)
        {
            if (string.IsNullOrWhiteSpace(appId))
                throw new ArgumentNullException("appId");
            if (string.IsNullOrWhiteSpace(apiName))
                throw new ArgumentNullException("apiName");
             string serviceKey = string.Format("{0}_{1}_{2}", appId, apiName, httpMethod);
             ServiceMetadata serviceMetadata = ServiceMetadatas.GetWithNoLock(serviceKey);
             if (serviceMetadata != null)
                 return serviceMetadata;

            if (!ServiceMetadataUpdated.ContainsKey(appId))
            {
                FullServiceMetadata(appId);
                Logger.Debug(string.Format("GetServiceMetadata From rpc.iapi.ymatou.com"));
            }
           
            return ServiceMetadatas.GetWithNoLock(serviceKey);
        }
    }
}
